// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <errno.h>

#include <fbl/algorithm.h>

#include "util.h"

#define MB (1 << 20)
#define PRINT_SIZE (MB * 100)

bool test_maxfile(void) {
  BEGIN_TEST;

  int fd = emu_open("::bigfile", O_CREAT | O_RDWR, 0644);
  ASSERT_GT(fd, 0);
  char data_a[8192];
  char data_b[8192];
  char data_c[8192];
  memset(data_a, 0xaa, sizeof(data_a));
  memset(data_b, 0xbb, sizeof(data_b));
  memset(data_c, 0xcc, sizeof(data_c));
  ssize_t sz = 0;
  ssize_t r;

  auto rotate = [&](const char* data) {
    if (data == data_a) {
      return data_b;
    } else if (data == data_b) {
      return data_c;
    } else {
      return data_a;
    }
  };

  const char* data = data_a;
  for (;;) {
    if ((r = emu_write(fd, data, sizeof(data_a))) < 0) {
      fprintf(stderr, "bigfile received error: %s\n", strerror(errno));
      if ((errno == EFBIG) || (errno == ENOSPC)) {
        // Either the file should be too big (EFBIG) or the file should
        // consume the whole volume (ENOSPC).
        fprintf(stderr, "(This was an expected error)\n");
        r = 0;
      }
      break;
    }
    if ((sz + r) % PRINT_SIZE < (sz % PRINT_SIZE)) {
      fprintf(stderr, "wrote %zu MB\n", static_cast<size_t>((sz + r) / MB));
    }
    sz += r;
    if (r < (ssize_t)(sizeof(data_a))) {
      fprintf(stderr, "bigfile write short write of %ld bytes\n", r);
      break;
    }

    // Rotate which data buffer we use
    data = rotate(data);
  }
  ASSERT_EQ(r, 0, "Saw an unexpected error from write");

  struct stat buf;
  ASSERT_EQ(emu_fstat(fd, &buf), 0, "Couldn't stat max file");
  ASSERT_EQ(buf.st_size, sz, "Unexpected max file size");

  // Try closing, re-opening, and verifying the file
  ASSERT_EQ(emu_close(fd), 0);
  fd = emu_open("::bigfile", O_RDWR, 0644);
  char readbuf[8192];
  ssize_t bytes_read = 0;
  data = data_a;
  while (bytes_read < sz) {
    r = emu_read(fd, readbuf, sizeof(readbuf));
    ASSERT_EQ(r, fbl::min(sz - bytes_read, static_cast<ssize_t>(sizeof(readbuf))));
    ASSERT_EQ(memcmp(readbuf, data, r), 0, "File failed to verify");
    data = rotate(data);
    bytes_read += r;
  }

  ASSERT_EQ(bytes_read, sz);

  ASSERT_EQ(emu_close(fd), 0);
  ASSERT_EQ(run_fsck(), 0);
  END_TEST;
}

RUN_MINFS_TESTS(maxfile_tests, RUN_TEST_LARGE(test_maxfile))
